package com.bonc.ioc.demo.filter;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @projectName: sdvp
 * @description: 解决XSS脚本攻击及sql盲注过滤器
 * @author: Liyy
 * @create: 2020-08-28 17:39
 **/
public class XssFilter implements Filter {

    private final String encoding = "UTF-8";
    private final String[] legalNames = "content1,ver,historyURL,listURL".split(",");
    private final String[] illegalChars = "|,$,@,',\",\\',\\\",<,>,(,),+,\\\",\",\\,http".split(",");

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse res = (HttpServletResponse) servletResponse;
        //必须手动指定编码格式
        req.setCharacterEncoding(encoding);
        String tempUrl = req.getRequestURI();
        Enumeration params = req.getParameterNames();

        //是否执行过滤  true：执行过滤  false：不执行过滤
        boolean executable = true;
        //非法状态  true：非法  false；不非法
        boolean illegalStatus = false;
        String illegalChar = "";
        //对参数名与参数进行判断
        w:
        while (params.hasMoreElements()) {
            String paramName = (String) params.nextElement();
            executable = true;
            //密码不过滤
            if (paramName.toLowerCase().contains("password")) {
                executable = false;
            } else {
                //检查提交参数的名字，是否合法，即不过滤其提交的值
                f:
                for (int i = 0; i < legalNames.length; i++) {
                    if (legalNames[i].equals(paramName)) {
                        executable = false;
                        break f;
                    }
                }
            }

            if (executable) {
                String[] paramValues = req.getParameterValues(paramName);
                f1:
                for (int i = 0; i < paramValues.length; i++) {
                    String paramValue = paramValues[i];
                    f2:
                    for (int j = 0; j < illegalChars.length; j++) {
                        illegalChar = illegalChars[j];
                        if (paramValue.indexOf(illegalChar) != -1 || checkXssAttack(paramValue)) {
                            //非法状态
                            illegalStatus = true;
                            break f2;
                        }
                    }
                    if (illegalStatus) {
                        break f1;
                    }
                }
            }

            if (illegalStatus) {
                break w;
            }
        }
        //对URL进行判断
        for (int j = 0; j < illegalChars.length; j++) {
            illegalChar = illegalChars[j];
            if ((tempUrl.indexOf(illegalChar) != -1) || checkXssAttack(tempUrl)) {
                //非法状态
                illegalStatus = true;
                break;
            }
        }
        if (illegalStatus) {
            res.sendRedirect(req.getContextPath() + "/sys/error/xss");
        } else {
            headerSet(req, res);
            filterChain.doFilter(req, res);
        }

    }

    /**
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     */
    public void headerSet(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setHeader("Cache-Control", "no-store");
        resp.setHeader("Pragma", "no-cache");
        resp.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
        resp.setHeader("Content-Security-Policy", "default-src 'self';script-src 'self' *://wso2.* *://*.wso2.*");
        if (req.getCookies() != null) {
            for (Cookie cookie : req.getCookies()) {
                cookie.setSecure(true);
                cookie.setHttpOnly(true);
                resp.addCookie(cookie);
            }
        }
    }

    @Override
    public void destroy() {

    }


    public boolean checkXssAttack(String str) {
        String[] regexArr = {"<(\\s)*(no)?script", "(no)?script(\\s)*>", "<(\\s)*iframe", "iframe(\\s)*>", "<(\\s)*img",
                "img(\\s)*>", "src(\\s)*=", "<(\\s)*a", "a(\\s)*>", "href(\\s)*=",
                "style(\\s)*=", "function\\(", "eval\\(", "expression\\(", "javascript:",
                "vbscript:", "view-source:", "window[\\s\\S]*location", "window\\.", "\\.location",
                "document[\\s\\S]*cookie", "document\\.", "alert\\(", ":alert", "window[\\s\\S]*open",
                "oncontrolselect", "oncopy", "oncut", "ondataavailable", "ondatasetchanged",
                "ondatasetcomplete", "ondblclick", "ondeactivate", "ondrag", "ondragend",
                "ondragenter", "ondragleave", "ondragover", "ondragstart", "ondrop",
                "onerror", "onerroupdate", "onfilterchange", "onfinish", "onfocus",
                "onfocusin", "onfocusout", "onhelp", "onkeydown", "onkeypress",
                "onkeyup", "onlayoutcomplete", "onload", "onlosecapture", "onmousedown",
                "onmouseenter", "onmouseleave", "onmousemove", "onmousout", "onmouseover",
                "onmouseup", "onmousewheel", "onmove", "onmoveend", "onmovestart",
                "onabort", "onactivate", "onafterprint", "onafterupdate", "onbefore",
                "onbeforeactivate", "onbeforecopy", "onbeforecut", "onbeforedeactivate", "onbeforeeditocus",
                "onbeforepaste", "onbeforeprint", "onbeforeunload", "onbeforeupdate", "onblur",
                "onbounce", "oncellchange", "onchange", "onclick", "oncontextmenu",
                "onpaste", "onpropertychange", "onreadystatechange", "onreset", "onresize",
                "onresizend", "onresizestart", "onrowenter", "onrowexit", "onrowsdelete",
                "onrowsinserted", "onscroll", "onselect", "onselectionchange", "onselectstart",
                "onstart", "onstop", "onsubmit", "onunload", "select",
                "insert", "delete", "from", "count\\(", "drop table",
                "update", "truncate", "asc\\(", "mid\\(", "char\\(",
                "xp_cmdshell", "exec", "master", "netlocalgroup administrators", "net user",
                "or", "and", "\\+", "'", "\""};
        for (String regex : regexArr) {
            //匹配完整单词，避免误伤
            if (Pattern.matches("\\w+", regex.trim())) {
                regex = "\\b" + regex.trim() + "\\b";
            }
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(str);
            if (matcher.find()) {
                return true;
            }
        }
        return false;
    }


}
